"""
项目级分析器
实现全项目的遍历、分析和语法树构建
"""

import os
import time
from pathlib import Path
from typing import Dict, List, Set, Optional, Any, Callable
from concurrent.futures import ThreadPoolExecutor, as_completed
import json
from dataclasses import asdict

from tree_sitter_config import Symbol, FileAnalysis
from python_parser import PythonParser
from symbol_indexer import SymbolIndex, ReferenceResolver, CodeMetrics


class ProjectAnalyzer:
    """项目级分析器"""
    
    def __init__(self, project_path: str, 
                 file_patterns: List[str] = None,
                 exclude_patterns: List[str] = None,
                 max_workers: int = 4):
        """
        初始化项目分析器
        
        Args:
            project_path: 项目根路径
            file_patterns: 包含的文件模式，默认为Python文件
            exclude_patterns: 排除的文件模式
            max_workers: 并行处理的最大工作线程数
        """
        self.project_path = Path(project_path).resolve()
        self.file_patterns = file_patterns or ["*.py"]
        self.exclude_patterns = exclude_patterns or [
            "__pycache__/*", "*.pyc", "venv/*", ".venv/*", 
            "env/*", ".env/*", "node_modules/*", ".git/*"
        ]
        self.max_workers = max_workers
        
        # 分析组件
        self.parser = PythonParser()
        self.symbol_index = SymbolIndex()
        self.reference_resolver = None
        self.code_metrics = None
        
        # 分析结果
        self.file_analyses: Dict[str, FileAnalysis] = {}
        self.analysis_summary = {}
        self.errors: List[str] = []
        
        # 性能统计
        self.timing_stats = {}
    
    def analyze_project(self, progress_callback: Optional[Callable[[str, float], None]] = None) -> Dict[str, Any]:
        """
        分析整个项目
        
        Args:
            progress_callback: 进度回调函数，参数为(状态消息, 进度百分比)
            
        Returns:
            分析结果字典
        """
        start_time = time.time()
        
        try:
            # 1. 发现文件
            if progress_callback:
                progress_callback("正在扫描项目文件...", 0)
            
            python_files = self._discover_files()
            total_files = len(python_files)
            
            if total_files == 0:
                self.errors.append("项目中没有找到Python文件")
                return self._create_summary()
            
            print(f"发现 {total_files} 个Python文件")
            
            # 2. 解析文件
            if progress_callback:
                progress_callback("正在解析文件...", 10)
            
            parse_start = time.time()
            self._parse_files(python_files, progress_callback)
            self.timing_stats["parse_time"] = time.time() - parse_start
            
            # 3. 构建符号索引
            if progress_callback:
                progress_callback("正在构建符号索引...", 60)
            
            index_start = time.time()
            self._build_symbol_index()
            self.timing_stats["index_time"] = time.time() - index_start
            
            # 4. 解析引用关系
            if progress_callback:
                progress_callback("正在解析引用关系...", 75)
            
            ref_start = time.time()
            self._resolve_references()
            self.timing_stats["reference_time"] = time.time() - ref_start
            
            # 5. 计算代码度量
            if progress_callback:
                progress_callback("正在计算代码度量...", 90)
            
            metrics_start = time.time()
            self._calculate_metrics()
            self.timing_stats["metrics_time"] = time.time() - metrics_start
            
            # 6. 生成摘要
            if progress_callback:
                progress_callback("正在生成分析报告...", 95)
            
            self.analysis_summary = self._create_summary()
            
            self.timing_stats["total_time"] = time.time() - start_time
            
            if progress_callback:
                progress_callback("分析完成", 100)
            
            print(f"项目分析完成，耗时 {self.timing_stats['total_time']:.2f} 秒")
            
        except Exception as e:
            self.errors.append(f"项目分析失败: {str(e)}")
            import traceback
            traceback.print_exc()
        
        return self.analysis_summary
    
    def _discover_files(self) -> List[str]:
        """发现项目中的Python文件"""
        python_files = []
        
        for pattern in self.file_patterns:
            for file_path in self.project_path.rglob(pattern):
                if file_path.is_file():
                    # 检查是否应该排除
                    relative_path = file_path.relative_to(self.project_path)
                    should_exclude = False
                    
                    for exclude_pattern in self.exclude_patterns:
                        if relative_path.match(exclude_pattern):
                            should_exclude = True
                            break
                    
                    if not should_exclude:
                        python_files.append(str(file_path))
        
        return sorted(python_files)
    
    def _parse_files(self, file_paths: List[str], 
                    progress_callback: Optional[Callable[[str, float], None]] = None):
        """并行解析文件"""
        completed = 0
        total = len(file_paths)
        
        def parse_single_file(file_path: str) -> Tuple[str, FileAnalysis]:
            """解析单个文件"""
            try:
                analysis = self.parser.parse_file(file_path)
                return file_path, analysis
            except Exception as e:
                error_analysis = FileAnalysis(file_path=file_path)
                error_analysis.errors.append(f"解析失败: {str(e)}")
                return file_path, error_analysis
        
        # 使用线程池并行处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_file = {executor.submit(parse_single_file, fp): fp 
                             for fp in file_paths}
            
            for future in as_completed(future_to_file):
                file_path, analysis = future.result()\n                self.file_analyses[file_path] = analysis\n                \n                completed += 1\n                progress = 10 + (completed / total) * 50  # 10% 到 60%\n                \n                if progress_callback:\n                    progress_callback(f\"已解析 {completed}/{total} 个文件\", progress)\n    \n    def _build_symbol_index(self):\n        \"\"\"构建符号索引\"\"\"\n        for file_path, analysis in self.file_analyses.items():\n            # 添加符号到索引\n            for symbol in analysis.symbols:\n                self.symbol_index.add_symbol(symbol)\n            \n            # 添加导入符号\n            for import_symbol in analysis.imports:\n                self.symbol_index.add_symbol(import_symbol)\n    \n    def _resolve_references(self):\n        \"\"\"解析引用关系\"\"\"\n        self.reference_resolver = ReferenceResolver(self.symbol_index, self.parser)\n        self.reference_resolver.resolve_references(self.file_analyses)\n    \n    def _calculate_metrics(self):\n        \"\"\"计算代码度量\"\"\"\n        if self.reference_resolver:\n            self.code_metrics = CodeMetrics(self.symbol_index, self.reference_resolver)\n    \n    def _create_summary(self) -> Dict[str, Any]:\n        \"\"\"创建分析摘要\"\"\"\n        summary = {\n            \"project_info\": {\n                \"path\": str(self.project_path),\n                \"files_analyzed\": len(self.file_analyses),\n                \"analysis_time\": self.timing_stats.get(\"total_time\", 0)\n            },\n            \"symbol_statistics\": self.symbol_index.get_statistics() if self.symbol_index else {},\n            \"file_analyses\": {},\n            \"errors\": self.errors,\n            \"timing_stats\": self.timing_stats\n        }\n        \n        # 添加文件分析结果\n        for file_path, analysis in self.file_analyses.items():\n            relative_path = str(Path(file_path).relative_to(self.project_path))\n            summary[\"file_analyses\"][relative_path] = analysis.to_dict()\n        \n        # 添加代码度量\n        if self.code_metrics:\n            try:\n                summary[\"complexity_metrics\"] = self.code_metrics.calculate_complexity_metrics()\n                summary[\"hotspots\"] = self.code_metrics.get_hotspots()\n            except Exception as e:\n                self.errors.append(f\"计算代码度量时出错: {str(e)}\")\n        \n        # 添加引用统计\n        if self.reference_resolver:\n            summary[\"reference_statistics\"] = {\n                \"total_references\": sum(len(refs) for refs in self.reference_resolver.references.values()),\n                \"call_graph_size\": len(self.reference_resolver.call_graph),\n                \"import_dependencies\": len(self.reference_resolver.import_graph)\n            }\n        \n        return summary\n    \n    def save_results(self, output_file: str, format: str = \"json\"):\n        \"\"\"保存分析结果\"\"\"\n        output_path = Path(output_file)\n        \n        if format.lower() == \"json\":\n            with open(output_path, 'w', encoding='utf-8') as f:\n                json.dump(self.analysis_summary, f, ensure_ascii=False, indent=2)\n        \n        elif format.lower() == \"html\":\n            self._generate_html_report(output_path)\n        \n        else:\n            raise ValueError(f\"不支持的输出格式: {format}\")\n    \n    def _generate_html_report(self, output_path: Path):\n        \"\"\"生成HTML报告\"\"\"\n        html_content = self._create_html_template()\n        \n        with open(output_path, 'w', encoding='utf-8') as f:\n            f.write(html_content)\n    \n    def _create_html_template(self) -> str:\n        \"\"\"创建HTML报告模板\"\"\"\n        stats = self.analysis_summary.get(\"symbol_statistics\", {})\n        project_info = self.analysis_summary.get(\"project_info\", {})\n        \n        return f\"\"\"\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Tree-sitter 项目分析报告</title>\n    <style>\n        body {{ font-family: Arial, sans-serif; margin: 20px; }}\n        .header {{ background-color: #f4f4f4; padding: 20px; border-radius: 5px; }}\n        .section {{ margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }}\n        .stats {{ display: flex; flex-wrap: wrap; gap: 20px; }}\n        .stat-box {{ background-color: #e9f5ff; padding: 15px; border-radius: 5px; min-width: 200px; }}\n        .file-list {{ max-height: 400px; overflow-y: auto; }}\n        .error {{ color: red; background-color: #ffe6e6; padding: 10px; border-radius: 5px; }}\n        table {{ width: 100%; border-collapse: collapse; }}\n        th, td {{ padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }}\n        th {{ background-color: #f2f2f2; }}\n    </style>\n</head>\n<body>\n    <div class=\"header\">\n        <h1>Tree-sitter 项目分析报告</h1>\n        <p><strong>项目路径:</strong> {project_info.get('path', 'N/A')}</p>\n        <p><strong>分析时间:</strong> {project_info.get('analysis_time', 0):.2f} 秒</p>\n        <p><strong>文件数量:</strong> {project_info.get('files_analyzed', 0)} 个</p>\n    </div>\n    \n    <div class=\"section\">\n        <h2>符号统计</h2>\n        <div class=\"stats\">\n            <div class=\"stat-box\">\n                <h3>总符号数</h3>\n                <p style=\"font-size: 24px; margin: 0;\">{stats.get('total_symbols', 0)}</p>\n            </div>\n            <div class=\"stat-box\">\n                <h3>函数数量</h3>\n                <p style=\"font-size: 24px; margin: 0;\">{stats.get('symbols_by_type', {}).get('function', 0)}</p>\n            </div>\n            <div class=\"stat-box\">\n                <h3>类数量</h3>\n                <p style=\"font-size: 24px; margin: 0;\">{stats.get('symbols_by_type', {}).get('class', 0)}</p>\n            </div>\n            <div class=\"stat-box\">\n                <h3>变量数量</h3>\n                <p style=\"font-size: 24px; margin: 0;\">{stats.get('symbols_by_type', {}).get('variable', 0)}</p>\n            </div>\n        </div>\n    </div>\n    \n    <div class=\"section\">\n        <h2>文件分析结果</h2>\n        <div class=\"file-list\">\n            <table>\n                <thead>\n                    <tr>\n                        <th>文件</th>\n                        <th>符号数</th>\n                        <th>导入数</th>\n                        <th>错误数</th>\n                    </tr>\n                </thead>\n                <tbody>\n\"\"\"\n        \n        # 添加文件列表\n        for file_path, analysis in self.analysis_summary.get(\"file_analyses\", {}).items():\n            html_content += f\"\"\"\n                    <tr>\n                        <td>{file_path}</td>\n                        <td>{analysis.get('symbols_count', 0)}</td>\n                        <td>{analysis.get('imports_count', 0)}</td>\n                        <td>{len(analysis.get('errors', []))}</td>\n                    </tr>\n\"\"\"\n        \n        html_content += \"\"\"\n                </tbody>\n            </table>\n        </div>\n    </div>\n\"\"\"\n        \n        # 添加错误信息\n        if self.errors:\n            html_content += \"\"\"\n    <div class=\"section\">\n        <h2>错误和警告</h2>\n\"\"\"\n            for error in self.errors:\n                html_content += f'<div class=\"error\">{error}</div>'\n            \n            html_content += \"</div>\"\n        \n        html_content += \"\"\"\n</body>\n</html>\n\"\"\"\n        \n        return html_content\n    \n    def query_symbols(self, name: str = None, symbol_type: str = None, \n                     file_path: str = None) -> List[Symbol]:\n        \"\"\"查询符号\"\"\"\n        if not self.symbol_index:\n            return []\n        \n        if name:\n            return self.symbol_index.find_symbols(name, symbol_type, file_path)\n        elif symbol_type:\n            return self.symbol_index.get_symbols_by_type(symbol_type)\n        elif file_path:\n            return self.symbol_index.get_symbols_in_file(file_path)\n        else:\n            return self.symbol_index.all_symbols\n    \n    def get_file_ast(self, file_path: str) -> Optional[Dict[str, Any]]:\n        \"\"\"获取文件的AST结构\"\"\"\n        analysis = self.file_analyses.get(file_path)\n        if analysis and analysis.ast:\n            try:\n                with open(file_path, 'rb') as f:\n                    source_code = f.read()\n                return self.parser.get_ast_structure(analysis.ast, source_code)\n            except Exception as e:\n                return {\"error\": f\"获取AST失败: {str(e)}\"}\n        return None\n    \n    def find_references(self, symbol_name: str) -> List[Dict[str, Any]]:\n        \"\"\"查找符号引用\"\"\"\n        if not self.reference_resolver:\n            return []\n        \n        references = self.reference_resolver.get_references(symbol_name)\n        result = []\n        \n        for file_path, ref_range in references:\n            relative_path = str(Path(file_path).relative_to(self.project_path))\n            result.append({\n                \"file\": relative_path,\n                \"range\": str(ref_range),\n                \"line\": ref_range.start.line,\n                \"column\": ref_range.start.column\n            })\n        \n        return result"